package com.personal.dao.base.impl;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import com.personal.dao.annotation.CacheNamespace;
import com.personal.dao.base.IBaseDao;
import com.personal.dao.bean.BaseModel;
import com.personal.dao.bean.Page;
import com.personal.dao.cache.Cache;
import com.personal.dao.cache.builder.CacheBuilder;
import com.personal.dao.configuration.Configuration;
import com.personal.dao.exception.IllegalQueryParamsException;
import com.personal.dao.handler.BatchSqlHandler;
import com.personal.dao.handler.SqlHandler;
import com.personal.dao.metadata.DataBaseTableMetadata;
import com.personal.dao.sql.BatchSql;
import com.personal.dao.sql.Sql;
import com.personal.dao.sqlgenerator.BatchInsert;
import com.personal.dao.sqlgenerator.BatchInsertSelective;
import com.personal.dao.sqlgenerator.BatchSqlGenerator;
import com.personal.dao.sqlgenerator.BatchUpdate;
import com.personal.dao.sqlgenerator.BatchUpdateSelective;
import com.personal.dao.sqlgenerator.CountByParams;
import com.personal.dao.sqlgenerator.DeleteByParams;
import com.personal.dao.sqlgenerator.DeleteByPrimaryKey;
import com.personal.dao.sqlgenerator.Insert;
import com.personal.dao.sqlgenerator.InsertSelective;
import com.personal.dao.sqlgenerator.SelectAll;
import com.personal.dao.sqlgenerator.SelectByParams;
import com.personal.dao.sqlgenerator.SelectByPrimaryKey;
import com.personal.dao.sqlgenerator.SelectByPrimaryKeyWithoutBigData;
import com.personal.dao.sqlgenerator.SqlGenerator;
import com.personal.dao.sqlgenerator.UpdateByParams;
import com.personal.dao.sqlgenerator.UpdateByParamsSelective;
import com.personal.dao.sqlgenerator.UpdateByPrimaryKey;
import com.personal.dao.sqlgenerator.UpdateByPrimaryKeySelective;
import com.personal.dao.template.CachingDaoTemplateBean;
import com.personal.dao.template.DaoTemplate;

/**
 * BaseDaoImpl
 * @author qq
 *
 */
public abstract class BaseDaoImpl<T extends BaseModel> implements IBaseDao<T>
{
    
    /** Dao模板 */
    private DaoTemplate template;
    
    /** 类 */
    private DataBaseTableMetadata<T> metadata;
     
    @Override
    public long countByParams(T params)
    {
        return execute(new CountByParams<>(params, metadata, template.getDaoDialect()), Configuration.getInstance().getDefaultQueryLongHandler());
    }

    @Override
    public int deleteByParams(T params)
    {
        return execute(new DeleteByParams<>(params, metadata, template.getDaoDialect()), Configuration.getInstance().getDefaultUpdateIntegerHandler());
    }

    @Override
    public int deleteByPrimaryKey(Object primaryKey)
    {
        return execute(new DeleteByPrimaryKey<>(primaryKey, metadata, template.getDaoDialect()), Configuration.getInstance().getDefaultUpdateIntegerHandler());
    }

    @Override
    public int insert(T record)
    {
        return execute(new Insert<>(record, metadata), Configuration.getInstance().getDefaultUpdateIntegerHandler());
    }
    
    @Override
    public int batchInsert(List<T> records)
    {
        return execute(new BatchInsert<>(records, metadata), Configuration.getInstance().getDefaultBatchUpdateIntegerHandler());
    }
    
    @Override
    public int batchInsertSelective(List<T> records)
    {
        return execute(new BatchInsertSelective<>(records, metadata), Configuration.getInstance().getDefaultBatchUpdateIntegerHandler());
    }

    @Override
    public int batchUpdate(List<T> records)
    {
        return execute(new BatchUpdate<>(records, metadata), Configuration.getInstance().getDefaultBatchUpdateIntegerHandler());
    }
    
    @Override
    public int batchUpdateSelective(List<T> records)
    {
        return execute(new BatchUpdateSelective<>(records, metadata), Configuration.getInstance().getDefaultBatchUpdateIntegerHandler());
    }
    
    @Override
    public int insertSelective(T record)
    {
        return execute(new InsertSelective<>(record, metadata), Configuration.getInstance().getDefaultUpdateIntegerHandler());
    }

    @Override
    public List<T> selectByParams(T params)
    {
        return (List<T>) execute(new SelectByParams<>(params, metadata, template.getDaoDialect()), Configuration.getInstance().newQueryListHandler(metadata));
    }

    @Override
	public List<T> selectAll()
	{
    	return (List<T>) execute(new SelectAll<>(metadata), Configuration.getInstance().newQueryListHandler(metadata));
	}

	@Override
    public T selectByPrimaryKey(Object primaryKey)
    {
        return execute(new SelectByPrimaryKey<>(primaryKey, metadata, template.getDaoDialect()), Configuration.getInstance().newQueryObjectHandler(metadata));
    }
    
    @Override
    public T selectByPrimaryKeyWithoutBigData(Object primaryKey)
    {
        return execute(new SelectByPrimaryKeyWithoutBigData<>(primaryKey, metadata, template.getDaoDialect()), Configuration.getInstance().newQueryObjectHandler(metadata));
    }

    @Override
    public int updateByParamsSelective(T record, T params)
    {
        return execute(new UpdateByParamsSelective<T>(record, params, metadata, template.getDaoDialect()), Configuration.getInstance().getDefaultUpdateIntegerHandler());
    }

    @Override
    public int updateByParams(T record, T params)
    {
        return execute(new UpdateByParams<T>(record, params, metadata, template.getDaoDialect()), Configuration.getInstance().getDefaultUpdateIntegerHandler());
    }

    @Override
    public int updateByPrimaryKeySelective(T record)
    {
        return execute(new UpdateByPrimaryKeySelective<T>(record, metadata, template.getDaoDialect()), Configuration.getInstance().getDefaultUpdateIntegerHandler());
    }

    @Override
    public int updateByPrimaryKey(T record)
    {
        return execute(new UpdateByPrimaryKey<T>(record, metadata, template.getDaoDialect()), Configuration.getInstance().getDefaultUpdateIntegerHandler());
    }

    @Override
	public Page<T> selectPageByParams(T params)
	{
    	if (params.getStart() == null || params.getLimit() == null)
		{
			throw new IllegalQueryParamsException("分页查询必须有start和limit参数！");
		}
    	long count = countByParams(params);
    	Page<T> result = new Page<T>();
    	result.setStart(params.getStart());
    	result.setLimit(params.getLimit());
    	result.setTotalCount(count);
    	if (count < 1)
		{
    		return result;
		}
    	result.setData(selectByParams(params));
    	return result;
	}
    
    private <V> V execute(BatchSqlGenerator sqlGenerator, BatchSqlHandler<V> handler)
   	{
       	return execute(sqlGenerator.generate(), handler);
   	}
    
    private <V> V execute(SqlGenerator sqlGenerator, SqlHandler<V> handler)
	{
    	return execute(sqlGenerator.generate(), handler);
	}

    @Override
	public <V> V execute(Sql sql, SqlHandler<V> handler)
	{
		return template.execute(sql, handler);
	}
    
    @Override
	public <V> V execute(BatchSql sql, BatchSqlHandler<V> handler)
	{
    	return template.execute(sql, handler);
	}
    
    @Override
	public DataBaseTableMetadata<T> getDataBaseTableMetadata()
	{
    	return metadata;
	}

    public DaoTemplate getDaoTemplate()
	{
		return template;
	}

	public void setTemplate(DaoTemplate template)
    {
        // 注册类信息至元数据缓存
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        @SuppressWarnings("unchecked")
		Class<T> c = (Class<T>)type.getActualTypeArguments()[0];
        this.metadata = Configuration.getInstance().registerDataBaseTableMetadata(c);
        // 解析缓存配置和设置DaoTemplate
        parseCacheAndSetTemplate(template);
    }

    private void parseCacheAndSetTemplate(DaoTemplate template)
    {
        CacheNamespace cacheNamespace = metadata.getC().getAnnotation(CacheNamespace.class);
        if (cacheNamespace != null)
        {
            Cache cache = new CacheBuilder(metadata.getC().getName(), cacheNamespace).build();
            this.template = new CachingDaoTemplateBean(template, cache);
        } else
        {
            this.template = template;
        }
    }

}